/************************************************************************
 * @file AbstractPlugin.cpp
 * @author Ian Chen
 * @version 1.0
 *
 * @section LICENSE
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 * General Public License for more details at
 * http://www.gnu.org/licenses/lgpl.html
 *
 ************************************************************************/
#include <QCoreApplication>
#include <QDir>
#include <QFileInfo>
#include <QLocale>

#include <plugin/plugin/AbstractPlugin.h>
#include <plugin/util/Framework.h>

namespace antu
{
namespace plugin
{
AbstractPlugin::AbstractPlugin()
    : QObject(nullptr),
      m_pPluginDescription(nullptr),
      m_pPluginContext(nullptr),
      m_pServiceRegistry(nullptr)
{

}

AbstractPlugin::AbstractPlugin(PluginDescriptionSharedPtr pDescription, ServiceRegistry* pServiceRegistry)
    : QObject(nullptr),
      m_pPluginDescription(pDescription),
      m_pPluginContext(nullptr),
      m_pServiceRegistry(pServiceRegistry)
{
    Q_ASSERT(pServiceRegistry != nullptr);
    this->connect(pServiceRegistry, SIGNAL(serviceAvailable(QString,QString,AbstractService*)),
                  this, SLOT(serviceFound(QString,QString,AbstractService*)));
}

AbstractPlugin::~AbstractPlugin()
{
    // manually delete all services provided by plugin
    map<QString, AbstractService*>::iterator iter = this->m_oServices.begin();
    map<QString, AbstractService*>::iterator end = this->m_oServices.end();

    for (; iter != end; iter++)
    {
        delete iter->second;
    }

    this->m_oServices.clear();
}

void AbstractPlugin::initialize(PluginContext *pContext)
{
    Q_ASSERT(pContext != nullptr);
    this->m_pPluginContext = pContext;

    this->doPreInit(pContext);
    this->doInit(pContext);
}

void AbstractPlugin::start()
{
    this->doStart();
}

void AbstractPlugin::finishStartup()
{
    this->doFinishStartup();
}

void AbstractPlugin::stop()
{
    this->doStop();
}

AbstractCommand::CommandReturnType AbstractPlugin::execute(const QString &commandLine, CommandParameters &outputs)
{
    int index = commandLine.indexOf(' ');
    if (index >= 0)
    {
        QString serviceName = commandLine.mid(0, index).trimmed();
        QString line = commandLine.mid(index).trimmed();

        map<QString, AbstractService*>::iterator iter = this->m_oServices.find(serviceName);
        if (iter == this->m_oServices.end())
        {
            return AbstractCommand::ServiceObjectNotFound;
        }
        else
        {
            return (iter->second)->execute(line, outputs);
        }
    }
    else
    {
        return AbstractCommand::CommandLineUnrecognized;
    }
}

AbstractCommand::CommandReturnType AbstractPlugin::execute(const QString &serviceName, const QString &commandName, const CommandParameters &inputs, CommandParameters &outputs)
{
    map<QString, AbstractService*>::iterator iter = this->m_oServices.find(serviceName);
    if (iter == this->m_oServices.end())
    {
        return AbstractCommand::ServiceObjectNotFound;
    }
    else
    {
        return (iter->second)->execute(commandName, inputs, outputs);
    }
}

void AbstractPlugin::doPreInit(PluginContext *pContext)
{
    this->doRegisterService(pContext);
    this->loadTranslationFile(this->description()->code());
}

void AbstractPlugin::doRegisterService(PluginContext *)
{
    // register services to service registry
    map<QString, AbstractService*>::iterator iter = this->m_oServices.begin();
    map<QString, AbstractService*>::iterator end = this->m_oServices.end();

    for (; iter != end; iter++)
    {
        if (Framework::Instance.securityContextHandler()->isAllowed(this->code(), iter->second->serviceName()))
            this->m_pServiceRegistry->registerService(this->code(), iter->second);
    }
}

void AbstractPlugin::requiredServiceFound(AbstractService *)
{

}

void AbstractPlugin::debug(const QString &file, int line, const QString &className, const QString &methodName, const QString &message) const
{
    LoggerService* pLogger = Framework::Instance.logger();
    if (pLogger != nullptr)
    {
        pLogger->debug(file, line, className, methodName, message);
    }
}

void AbstractPlugin::info(const QString &file, int line, const QString &className, const QString &methodName, const QString &message) const
{
    LoggerService* pLogger = Framework::Instance.logger();
    if (pLogger != nullptr)
    {
        pLogger->info(file, line, className, methodName, message);
    }
}

void AbstractPlugin::warning(const QString &file, int line, const QString &className, const QString &methodName, const QString &message) const
{
    LoggerService* pLogger = Framework::Instance.logger();
    if (pLogger != nullptr)
    {
        pLogger->warn(file, line, className, methodName, message);
    }
}

void AbstractPlugin::error(const QString &file, int line, const QString &className, const QString &methodName, const QString &message) const
{
    LoggerService* pLogger = Framework::Instance.logger();
    if (pLogger != nullptr)
    {
        pLogger->error(file, line, className, methodName, message);
    }
}

void AbstractPlugin::fatal(const QString &file, int line, const QString &className, const QString &methodName, const QString &message) const
{
    LoggerService* pLogger = Framework::Instance.logger();
    if (pLogger != nullptr)
    {
        pLogger->fatal(file, line, className, methodName, message);
    }
}

bool AbstractPlugin::loadTranslationFile(const QString &translationFile)
{
    Q_ASSERT( this->context() != nullptr );

    QFileInfo info(translationFile);
    if (info.isAbsolute())
    {
        QString completeName = QString("%1_%2").arg(translationFile).arg(QLocale::system().name());
        bool result = m_oTranslator.load(completeName);
        qApp->installTranslator(&m_oTranslator);
        return result;
    }
    else
    {
        QDir dir(this->context()->languagePath());
        QString translateFileName = QString("%1_%2").arg(translationFile).arg(QLocale::system().name());
        bool result = m_oTranslator.load(dir.filePath(translateFileName));
        qApp->installTranslator(&m_oTranslator);
        return result;
    }
}

void AbstractPlugin::serviceFound(const QString &pluginCode, const QString &serviceName, AbstractService *pService)
{
    Q_ASSERT(pService != nullptr);

    int count = this->description()->requirementCount();
    for (int i = 0; i < count; i++)
    {
        PluginRequirement requirement = this->description()->requirementAt(i);

        if (requirement.code() == pluginCode &&
                requirement.needsService(serviceName))
        {
            requiredServiceFound(pService);
        }
    }
}

}
}
